#include "priv.h"

#include "cmdop.h"
#include "cli_log.h"

static cmd_vty_t *g_vty;

char *cmd_prompt(cmd_vty_t *vty)
{
    cmd_node_t *cnode;
    cnode = vector_slot(vty->topvec, vty->node);
    return cnode->prompt;
}

void cmd_vty_init(cmd_vty_t *vty)
{
    cli_log_init(7, "cmd_client");

    vty->topvec = vector_init(VECTOR_MIN_SIZE);
    vty->status = VTY_NORMAL;
    g_vty = vty;
}

void cmd_free_strvec (vector v)
{
    unsigned i;
    char *cp; 

    if (!v) 
    {
        return;
    }

    for (i = 0; i < vector_max (v); i++) 
    {
        if ((cp = vector_slot (v, i)) != NULL)
        {
            free (cp);
        }
    }

    vector_free (v);
}

static int cmd_unique_string(vector v, char *str)
{
    unsigned i;
    char *match;

    for (i = 0; i < vector_max(v); i++) 
    {
        if ((match = vector_slot(v, i)) != NULL)
        {
            if (strcmp(match, str) == 0)
            {
                return 0;
            }
        }
    }

    return 1;
}
static vector cmd_make_descvec(char *string)
{
    char *sp;
    char *token;
    int len;
    char *cp;
    vector strvec = NULL;

    cp = string;

    if (cp == NULL || *cp == '\0')
    {
        return NULL;
    }

    strvec = vector_init(VECTOR_MIN_SIZE);

    while (1)
    {
        while (isspace ((int) *cp) && *cp != '\0')
        {
            cp++;
        }

        if (*cp == '\0')
        {
            return strvec;
        }

        sp = cp;

        while (!(isspace ((int) *cp) || *cp == '\r' || *cp == '\n') && *cp != '\0')
        {
            cp++;
        }

        len = cp - sp;
        if (len <= 0)
        {
            continue;
        }

        token = calloc(1, len + 1);
        memcpy (token, sp, len);
        *(token + len) = '\0';

        vector_set(strvec, token);
    }
}
vector cmd_make_strvec(char *string)
{
    char *cp, *start, *token, *tstart, *tend;
    int str_len;
    vector strvec;
    char *string2;

    if (string == NULL)
    {
        return NULL;
    }

    string2 = strdup(string);
    if((tstart = strchr(string2, '"')) != NULL)
    {
        tend = strchr(tstart + 1, '"');
        if(tend != NULL)
        {
            *tstart = ' ';
            tstart ++;
            while(tstart < tend)
            {
                if(*tstart == ' ')
                {
                    *tstart = '-';
                }
                tstart ++;
            }
            *tend = ' ';
        }
    }
    cp = string2;

    while (isspace ((int) *cp) && *cp != '\0')
    {
        cp++;
    }

    if (*cp == '\0')
    {
        return NULL;
    }

    if (*cp == '!' || *cp == '#')
    {
        return NULL;
    }

    strvec = vector_init (VECTOR_MIN_SIZE);

    while (1)
    {
        start = cp;
        while (!(isspace ((int) *cp) || *cp == '\r' || *cp == '\n') &&
                *cp != '\0')
        {
            cp++;
        }
        str_len = cp - start;
        token = calloc (1, str_len + 1);
        memcpy (token, start, str_len);
        *(token + str_len) = '\0';
        vector_set (strvec, token);

        while ((isspace ((int) *cp) || *cp == '\n' || *cp == '\r') &&
                *cp != '\0')
        {
            cp++;
        }

        if (*cp == '\0')
        {
            free(string2);
            return strvec;
        }
    }
}


static int cmd_filter_by_completion (char *string, vector v, int index)
{
    cmd_element_t *cmd_element;
    char *command;
    int matched = 0;
    unsigned i;

    for (i = 0; i < vector_max(v); i++)
    {
        if ((cmd_element = vector_slot(v, i)) == NULL)
        {
            continue;
        }

        if ((unsigned)index >= vector_max(cmd_element->strvec))
        {
            vector_slot (v, i) = NULL;
            continue;
        }

        command = vector_slot(cmd_element->strvec, index);

        if ((command[0] < 'A' || command[0] > 'Z') && strcmp(string, command) != 0)
        {
            vector_slot(v, i) = NULL;
        } 
        else
        {
            matched = 1;
        }
    }

    return matched;
}
char **cmd_complete_command (vector vline, cmd_vty_t *vty, int *status)
{
    cmd_element_t *cmd_element;
    vector matchvec;
    char **match_str;
    char *command;
    char *string;
    int index = vector_max (vline) - 1;
    int i;

    cmd_node_t *cnode = vector_slot (vty->topvec, vty->node);
    vector cmd_vector = vector_copy(cnode->cmdvec);

    for (i = 0; i < index; i++)
    {
        string = vector_slot(vline, i);
        if (!cmd_filter_by_completion(string, cmd_vector, i))
        {
            vector_free (cmd_vector);
            *status = CMD_ERR_NO_MATCH;
            return NULL;
        }
    }

    matchvec = vector_init(VECTOR_MATCHVEC_SIZE);

    for (i = 0; i < (int)vector_max(cmd_vector); i++)
    {
        if ((cmd_element = vector_slot (cmd_vector, i)) == NULL)
        {
            continue;
        }

        if ((unsigned)index >= vector_max(cmd_element->strvec))
        {
            vector_slot (cmd_vector, i) = NULL;
            continue;
        }

        command = vector_slot(cmd_element->strvec, index);
        string = vector_slot(vline, index);

        if ((command[0] < 'A' || command[0] > 'Z') && 
                (!string || strncmp(string, command, strlen(string)) == 0))
        {
            if (cmd_unique_string (matchvec, command))
            {
                vector_set(matchvec, strdup (command));
            }
        }
    }

    vector_free(cmd_vector);

    if (vector_slot(matchvec, 0) == NULL)
    {
        vector_free(matchvec);
        if (vector_slot(vline, index) == '\0')
        {
            *status = CMD_ERR_NOTHING_TODO;
        }
        else
        {
            *status = CMD_ERR_NO_MATCH;
        }

        return NULL;
    }

    if (vector_slot(matchvec, 1) == NULL)
    {
        match_str = (char **) matchvec->index;
        vector_only_wrapper_free(matchvec);
        *status = CMD_COMPLETE_FULL_MATCH;
        return match_str;
    }

    vector_set (matchvec, NULL);

    match_str = (char **) matchvec->index;
    vector_only_wrapper_free (matchvec);
    *status = CMD_COMPLETE_LIST_MATCH;
    return match_str;
}

int cmd_execute_command(vector vline, cmd_vty_t *vty)
{
    cmd_element_t *cmd_element;
    cmd_element_t *matched_element;
    char *command;
    char *string;
    char *argv[CMD_ARGC_MAX];
    int argc = 0;
    unsigned int matched_count;
    unsigned int incomplete_count;
    int i;

    cmd_node_t *cnode = vector_slot (vty->topvec, vty->node);
    vector cmd_vector = vector_copy(cnode->cmdvec);

    for (i = 0; i < (int)vector_max(vline); i++)
    {
        string = vector_slot(vline, i);
        if (!cmd_filter_by_completion(string, cmd_vector, i))
        {
            vector_free (cmd_vector);
            return CMD_ERR_NO_MATCH;
        }
    }

    matched_element = NULL;
    matched_count = 0;
    incomplete_count = 0;

    for (i = 0; i < (int)vector_max(cmd_vector); i++)
    {
        if ((cmd_element = vector_slot (cmd_vector, i)) == NULL)
        {
            continue;
        }

        if (vector_max(vline) == vector_max(cmd_element->strvec))
        {
            matched_element = cmd_element;
            matched_count++;
        }
        else
        {
            incomplete_count++;
        }
    }

    vector_free(cmd_vector);

    if (matched_count == 0)
    {
        if (incomplete_count)
        {
            return CMD_ERR_INCOMPLETE;
        }
        else
        {
            return CMD_ERR_NO_MATCH;
        }
    }

    if (matched_count > 1)
    {
        return CMD_ERR_AMBIGUOUS;
    }

    for (i = 0; i < (int)vector_max(vline); i++)
    {
        command = vector_slot(matched_element->strvec, i);
        if (command[0] >= 'A' && command[0] <= 'Z') 
        {
            argv[argc++] = vector_slot(vline, i); 
        } 

        if (argc >= CMD_ARGC_MAX)
        {
            return CMD_ERR_EXEED_ARGC_MAX;
        }
    }

    return (*matched_element->exec)(vty, argc, argv);
}

void cmd_execute(cmd_vty_t *vty, char *line)
{
    int ret;
    vector vline;

    vline = cmd_make_strvec (line);
    if (vline == NULL)
    {
        return;
    }

    ret = cmd_execute_command(vline, vty);

    cmd_free_strvec (vline);

    switch (ret)
    {
        case CMD_WARNING:
            printf ("Warning...\n");
            break;
        case CMD_ERR_AMBIGUOUS:
            printf ("%% Ambiguous command.\n");
            break;
        case CMD_ERR_NO_MATCH:
            printf ("%% Unknown command.\n");
            break;
        case CMD_ERR_INCOMPLETE:
            printf ("%% Command incomplete.\n");
            break;
        case  CMD_ERR_EXEED_ARGC_MAX:
            printf("%% Command exeed argc max.\n");
            break;
    }
}

void cmd_install_node(cmd_vty_t *vty, cmd_node_t *node)
{
    vector_set_index (vty->topvec, node->node, node);
    node->cmdvec = vector_init (VECTOR_MIN_SIZE);
}

void cmd_install_element(vector topvec, int ntype, cmd_element_t *cmd)
{
    cmd_node_t *cnode;

    cnode = vector_slot(topvec, ntype);
    if (cnode == NULL)
    {    
        CLI_ERRLOG("Command node %d doesn't exist, please check it\n", ntype);
        exit (1);
    }    

    vector_set(cnode->cmdvec, cmd);

    cmd->strvec = cmd_make_descvec(cmd->string);
}

void cmd_doc_print(cmd_vty_t *vty, FILE *fp)
{
    cmd_node_t *cnode;
    cmd_element_t *element;
    unsigned i;

    cnode = (cmd_node_t *)vector_slot(vty->topvec, vty->node); 

    for (i = 0; i < vector_max(cnode->cmdvec); i++) 
    {
        element = vector_slot(cnode->cmdvec, i);
        fprintf(fp, "    %s\n", element->string);
        fprintf(fp, "        --%s\n\n", element->doc);
    }
}
